﻿/*
 * DATAFLOWCORE
 * 
Copyright 2012 - Mindstorm Multitouch Limited

Author - Bertrand Nouvel

DataFlowCore is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

DataFlowCore is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser Public License for more details.
*/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DFlowCore
{

    /// <summary>
    /// This is a minimal implementation multidimensional array class.
    /// These array can be used for compatibility in-between different frameworks.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ArrayView<T> : IDisposable, IEnumerable, IEnumerable<T>
        where T:struct
    {
        
        //ArrayView<T> refarray=null;
        bool instantiated=false;


        public int[] offsets;
        public int[] strides;
        public int[] lengths;
        //public T[] data;
        public System.IntPtr dataptr;

        public int[] Shape { get { return lengths; } }
        public int[] Stride { get { return strides; } }
        public int ndim { get { return lengths.Length; } }

        public MongoDB.Bson.BsonValue ToBson() { 
            MongoDB.Bson.BsonDocument bd =new MongoDB.Bson.BsonDocument();
            if (TotalElements == 0)
            {
                return MongoDB.Bson.BsonNull.Value;
            }
            bd.Add("lengths", new MongoDB.Bson.BsonArray(lengths.Select(x => new MongoDB.Bson.BsonInt32(x))));
            int ld=TotalElements* System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            byte [] data=new byte[ld];
            System.Runtime.InteropServices.Marshal.Copy(dataptr, data, 0, ld);
            bd.Add("data", new MongoDB.Bson.BsonBinaryData(data));
            return bd;
        }

        public int TotalElements
        {
            get
            {
                if (ndim == 0) return 0;
                int r = 1;
                for (int i = 0; i < ndim; i++) r *= lengths[i];
                return r;
            }
        }


        public int LinearAddr(int [] coords) {
            int r=0;
            for (int i=0;i<ndim;i++) {
                r+=(offsets[i]+coords[i])*strides[i];
            }
            return r;
        }

         ~ArrayView()
        {
            if (instantiated)
            {
                System.Runtime.InteropServices.Marshal.FreeHGlobal(dataptr);
            }
        }


         public void Dispose()
         {
             if (instantiated)
             {
                 System.Runtime.InteropServices.Marshal.FreeHGlobal(dataptr);
                 dataptr = System.IntPtr.Zero;
             }
             instantiated = false;             
             strides=offsets=lengths = new int[] { };
         }


        public unsafe void Reset() {
            int nb=TotalElements * System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            byte * b=(byte*)dataptr.ToPointer();
            for (int i=0;i<nb;i++) {
                *b=0;b++;
            }
        }

        public unsafe ArrayView(params int [] shape) {
            
            lengths=shape;
            offsets=new int[shape.Length];
            strides=new int[shape.Length];
            instantiated = true;
            int nb=TotalElements * System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            dataptr = System.Runtime.InteropServices.Marshal.AllocHGlobal(nb);

            Reset();            
            int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)); 
            for(int i=ndim-1; i>=0; i--) {
                strides[i] = r;
                r*=lengths[i];                
            }
        }

        ArrayView(System.IntPtr scan0,params int [] shape) {
            //refarray = null;
            lengths=shape;
            offsets=new int[shape.Length];
            strides=new int[shape.Length];
            dataptr = scan0;

            instantiated = false;

            int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            for (int i = ndim - 1; i >= 0; i--)
            {
                strides[i] = r;
                r*=lengths[i];                
            }
        }


        ArrayView(ArrayView<T> orig)
        {
            if (orig.IsContiguous)
            {
                //refarray = null;
                lengths = orig.lengths;
                offsets = new int[orig.lengths.Length];
                strides = new int[orig.lengths.Length];
                dataptr = orig.dataptr;

                instantiated = false;

                int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
                for (int i = ndim - 1; i >= 0; i--)
                {
                    strides[i] = r;
                    r *= lengths[i];
                }
            }
            else
            {
                throw new System.NotImplementedException();
            }
        }
        public bool IsContiguous
        {
            get {
                int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
                for (int i = ndim - 1; i >= 0; i--)
                {
                    if (offsets[i] != 0) { return false; }
                    if (strides[i] != r) { return false;  }
                    r*=lengths[i];                
                }
                return true;
            }
        }


        /*
        ArrayView(ArrayView<T> orig, params int[] newshape)
        {
            refarray = orig; // do not uninstantiate the array while still in use
            dataptr=orig.dataptr;            
            lengths=newshape;
            if (orig.TotalElements!=TotalElements) {
                throw new System.ArgumentException("Arrays do not have the same number of elements");
            }
            offsets=orig.offsets;
            strides=new int[newshape.Length];
        }
        */

        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] 
        class TempStorage {
            public T temp;
        }

        IEnumerator IEnumerable.GetEnumerator() {
            int te=TotalElements;
            int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            TempStorage ts=new TempStorage();
            for (int i = 0; i < te; i++)
            {
                System.Runtime.InteropServices.Marshal.PtrToStructure(dataptr+(i*r),ts);
                yield return ts.temp;  //((byte*)dataptr)[i * r];                
            }
        }



        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            int te = TotalElements;
            int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            TempStorage ts = new TempStorage();
            if (dataptr != System.IntPtr.Zero)
            {
                for (int i = 0; i < te; i++)
                {
                    System.Runtime.InteropServices.Marshal.PtrToStructure(dataptr + (i * r), ts);
                    yield return ts.temp;  //((byte*)dataptr)[i * r];                
                }
            }
            // else throw new System.NullReferenceException();
        }
        

        static public unsafe ArrayView<T>  operator + (ArrayView<T> a , ArrayView<T> b) {
            System.Diagnostics.Debug.Assert(a.Shape==b.Shape);
            int te=a.TotalElements;
            TempStorage tsa = new TempStorage();
            TempStorage tsb = new TempStorage();
            TempStorage tsc = new TempStorage();
            ArrayView<T> res=new ArrayView<T>(a.Shape);
            int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            for(int i=0;i<te;i++) {
                System.Runtime.InteropServices.Marshal.PtrToStructure(a.dataptr + (i * r), tsa);
                System.Runtime.InteropServices.Marshal.PtrToStructure(b.dataptr + (i * r), tsb);
                tsc.temp = Operator.Add<T>(tsa.temp, tsb.temp);
                System.Runtime.InteropServices.Marshal.StructureToPtr(tsc,res.dataptr + (i * r),false);
            }
            return res;
        }


        static public unsafe ArrayView<T> operator -(ArrayView<T> a, ArrayView<T> b)
        {
            System.Diagnostics.Debug.Assert(a.Shape == b.Shape);
            int te = a.TotalElements;
            TempStorage tsa = new TempStorage();
            TempStorage tsb = new TempStorage();
            TempStorage tsc = new TempStorage();
            ArrayView<T> res = new ArrayView<T>(a.Shape);
            int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            for (int i = 0; i < te; i++)
            {
                System.Runtime.InteropServices.Marshal.PtrToStructure(a.dataptr + (i * r), tsa);
                System.Runtime.InteropServices.Marshal.PtrToStructure(b.dataptr + (i * r), tsb);
                tsc.temp = Operator.Subtract<T>(tsa.temp, tsb.temp);
                System.Runtime.InteropServices.Marshal.StructureToPtr(tsc, res.dataptr + (i * r), false);
            }
            return res;
        }


        static public unsafe ArrayView<T> operator *(ArrayView<T> a, ArrayView<T> b)
        {
            System.Diagnostics.Debug.Assert(a.Shape == b.Shape);
            int te = a.TotalElements;
            TempStorage tsa = new TempStorage();
            TempStorage tsb = new TempStorage();
            TempStorage tsc = new TempStorage();
            ArrayView<T> res = new ArrayView<T>(a.Shape);
            int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            for (int i = 0; i < te; i++)
            {
                System.Runtime.InteropServices.Marshal.PtrToStructure(a.dataptr + (i * r), tsa);
                System.Runtime.InteropServices.Marshal.PtrToStructure(b.dataptr + (i * r), tsb);
                tsc.temp = Operator.Multiply<T>(tsa.temp, tsb.temp);
                System.Runtime.InteropServices.Marshal.StructureToPtr(tsc, res.dataptr + (i * r), false);
            }
            return res;
        }

        static public unsafe ArrayView<T> operator /(ArrayView<T> a, ArrayView<T> b)
        {
            System.Diagnostics.Debug.Assert(a.Shape == b.Shape);
            int te = a.TotalElements;
            TempStorage tsa = new TempStorage();
            TempStorage tsb = new TempStorage();
            TempStorage tsc = new TempStorage();
            ArrayView<T> res = new ArrayView<T>(a.Shape);
            int r = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T));
            for (int i = 0; i < te; i++)
            {
                System.Runtime.InteropServices.Marshal.PtrToStructure(a.dataptr + (i * r), tsa);
                System.Runtime.InteropServices.Marshal.PtrToStructure(b.dataptr + (i * r), tsb);
                tsc.temp = Operator.Divide<T>(tsa.temp, tsb.temp);
                System.Runtime.InteropServices.Marshal.StructureToPtr(tsc, res.dataptr + (i * r), false);
            }
            return res;
        }

    }
}
